package Negocio;

import entidades.Administrador;
import entidades.Administrativo;
import entidades.Cita;
import entidades.Enfermedad;
import entidades.HistoriaClinica;
import entidades.ListaDeEspera;
import entidades.Medico;
import entidades.Mensaje;
import entidades.Paciente;
import entidades.Tratamiento;
import entidades.Usuario;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class Negocio implements InterfazNegocio {

    @PersistenceContext(unitName = "Tarea3-ejbPU")
    private EntityManager em;

    @Override
    public Error registrarUsuario(Usuario u) {
        Usuario user = em.find(Usuario.class, u.getDni());
        if (user != null) {
            return Error.ERROR_USUARIO_REPETIDO;
        }

        em.persist(u);

        return Error.NO_ERROR;
    }

    @Override
    public Error compruebaLogin(Usuario u) {
        Usuario user = em.find(Usuario.class, u.getDni());
        if (user == null) {
            return InterfazNegocio.Error.ERROR_USUARIO_INEXISTENTE;
        }
        if (user.getPassword().equals(u.getPassword())) {
            return InterfazNegocio.Error.NO_ERROR;
        } else {
            return InterfazNegocio.Error.CONTRASEÑA_ERRONEA;
        }

    }

    @Override
    public Error modificar_Datos_Personales(Usuario u, String telefono, String email, String contraseña) {

        Usuario user = em.find(Usuario.class, u.getDni());
        if (user == null) {

            return InterfazNegocio.Error.ERROR_USUARIO_INEXISTENTE;
        } else {
            System.out.println(user);
            user.setEmail(email);
            user.setTelefono(telefono);
            user.setPassword(contraseña);
            em.merge(user);
            return Error.NO_ERROR;
        }

    }

    @Override
    public Error crear_Lista_Espera(ListaDeEspera l) {
        ListaDeEspera list = em.find(ListaDeEspera.class, l.getNombre());
        if (list != null) {
            return Error.ERROR_LISTA_REPETIDA;
        }

        em.persist(l);
        return Error.NO_ERROR;
    }

    @Override
    public Error insertar_Lista_Espera(Paciente p, ListaDeEspera l) {
        Error e = compruebaLogin(p);
        if (e != Error.NO_ERROR) {
            return e;
        } else {
            ListaDeEspera lista = em.find(ListaDeEspera.class, l.getNombre());
            if (lista == null) {
                return Error.NO_EXISTE_LISTA_ESPERA;
            } else {
                List<Paciente> pacientes = lista.getPaciente();
                if (pacientes.contains(p)) {
                    return Error.ERROR_USUARIO_REPETIDO;
                } else {
                    lista.setNumeroPacientes(lista.getNumeroPacientes() + 1);
                    pacientes.add(p);
                    lista.setPaciente(pacientes);
                    em.persist(lista);
                    return Error.NO_ERROR;
                }
            }
        }
    }

    @Override
    public Error eliminarUsuario(Usuario u) {
        Error e = compruebaLogin(u);

        if (e != InterfazNegocio.Error.NO_ERROR) {
            return e;
        } else {
            Usuario us = em.find(Usuario.class, u.getDni());
            if (us == null) {
                return Error.ERROR_USUARIO_INEXISTENTE;
            } else {
                em.remove(us);
                return Error.NO_ERROR;
            }
        }
    }

    @Override
    public InterfazNegocio.Error insertar_Lista_Paciente(Paciente p, Medico m) {
        Medico medico = em.find(Medico.class, m.getDni());
        if (medico.getPacientes().contains(p)) {
            return Error.ERROR_USUARIO_REPETIDO;
        } else {
            medico.getPacientes().add(p);
            medico.setPacientes(medico.getPacientes());
            em.merge(medico);
            return Error.NO_ERROR;
        }
    }

    @Override
    public Error eliminar_Paciente_Lista_Espera(Paciente p, ListaDeEspera l) {
        Error e = compruebaLogin(p);

        if (e != InterfazNegocio.Error.NO_ERROR) {
            return e;
        } else {
            ListaDeEspera lista = em.find(ListaDeEspera.class, l.getNombre());
            if (lista == null) {
                return Error.NO_EXISTE_LISTA_ESPERA;
            } else {
                List<Paciente> pacientes = lista.getPaciente();
                if (pacientes.contains(p)) {
                    pacientes.remove(p);
                    lista.setPaciente(pacientes);
                    lista.setNumeroPacientes(lista.getNumeroPacientes() - 1);
                    em.merge(lista);
                    return Error.NO_ERROR;
                } else {
                    return Error.NO_PACIENTE_LISTA_ESPERA;
                }
            }
        }
    }

    @Override
    public Error eliminar_Paciente_Lista_Paciente(Paciente p, Medico m) {
        Medico medico = em.find(Medico.class, m.getDni());
        if (medico == null) {
            return Error.ERROR_USUARIO_INEXISTENTE;
        } else {
            List<Paciente> pacientes = medico.getPacientes();
            if (pacientes.contains(p)) {
                pacientes.remove(p);
                medico.setPacientes(pacientes);
                em.merge(medico);
                return Error.NO_ERROR;
            } else {
                return Error.NO_PACIENTE_LISTA_ESPERA;
            }
        }
    }

    @Override
    public Error modificar_Historia_Clinica(Paciente p, String descripcion, Enfermedad enf, Tratamiento t, String dni) {
        HistoriaClinica h = em.find(HistoriaClinica.class, p.gethClinica().getId_Historia_Clinica());

        List<Enfermedad> enfermedad = p.gethClinica().getEnfermedad();
        if (enfermedad.contains(enf)) {
            return Error.ERROR_ENFERMEDAD_REPETIDA;
        } else {
            h.setDescripcion(descripcion);
            h.setDNIUltimoAcceso(dni);
            h.getEnfermedad().add(enf);

            em.persist(t);
            Tratamiento trat = em.find(Tratamiento.class, t.getId_Tratamiento());

            h.getTratamiento().add(trat);

            em.merge(h);
        }

        return Error.NO_ERROR;
    }

    @Override
    public Error bloquear_Historia_Clinica(Paciente p, boolean b) {
        HistoriaClinica h = em.find(HistoriaClinica.class, p.gethClinica().getId_Historia_Clinica());
        h.setBloqueado(b);

        em.merge(h);

        return Error.NO_ERROR;
    }

    @Override
    public Error mandar_mensaje(Mensaje m, String dni) {
        Error e = compruebaLogin(m.getEmisor());
        if (e != Error.NO_ERROR) {
            return e;
        }

        m.setDeletedByEmisor(Boolean.FALSE);
        m.setDeletedByRecibe(Boolean.FALSE);

        em.persist(m);

        Usuario recibe = null;

        if (dni != null) {
            recibe = (Usuario) em.createQuery("SELECT u FROM Usuario u WHERE u.dni = :dni")
                    .setParameter("dni", dni)
                    .getSingleResult();
            //TODO nenajde ho - inexistent
        }

        m.setRecibe(recibe);

        //update emisor
        Usuario emisor = m.getEmisor();
        emisor = em.find(emisor.getClass(), emisor.getDni());
        emisor.getEnviados().add(m);
        em.merge(emisor);

        //update recibe
        if (recibe != null) {
            recibe.getEnviados().add(m);
            em.merge(recibe);
        }

        return Error.NO_ERROR;
    }

    @Override
    public Error eliminar_recibido(Long id, Usuario u) {
        Error e = compruebaLogin(u);
        if (e != Error.NO_ERROR) {
            return e;

        }

        Mensaje m = em.find(Mensaje.class, id);
        m.setDeletedByRecibe(Boolean.TRUE);

        //both emisor and recibe are deleted -> delete message
        if (m.isDeletedByEmisor()) {
            em.remove(m);
        } else {
            em.merge(m);
        }

        return Error.NO_ERROR;
    }

    @Override
    public Error eliminar_enviado(Long id, Usuario u) {
        Error e = compruebaLogin(u);
        if (e != Error.NO_ERROR) {
            return e;

        }

        Mensaje m = em.find(Mensaje.class, id);
        m.setDeletedByEmisor(Boolean.TRUE);

        //both emisor and recibe are deleted -> delete message
        if (m.isDeletedByRecibe()) {
            em.remove(m);
        } else {
            em.merge(m);
        }

        return Error.NO_ERROR;
    }

    @Override
    public Error eliminar_queja(Long id, Usuario u) {
        Error e = compruebaLogin(u);
        if (e != Error.NO_ERROR) {
            return e;

        }

        Mensaje m = em.find(Mensaje.class, id);
        m.setDeletedByRecibe(Boolean.TRUE);

        //both emisor and recibe are deleted -> delete message
        if (m.isDeletedByEmisor()) {
            em.remove(m);
        } else {
            em.merge(m);
        }

        return Error.NO_ERROR;
    }

    @Override
    public List<Mensaje> getQuejas(Usuario u) {
        Error e = compruebaLogin(u);
        if (e != Error.NO_ERROR) {
            return null;
        }

        return em.createQuery("SELECT m FROM Mensaje m WHERE m.tipo = entidades.Mensaje.Tipo.queja AND m.deletedByRecibe = FALSE").getResultList();
    }

    @Override
    public List<Mensaje> getRecibidos(Usuario u) {
        return em.createQuery("SELECT m FROM Mensaje m WHERE m.recibe = :usr AND m.deletedByRecibe = FALSE").
                setParameter("usr", u).getResultList();
    }

    @Override
    public List<Mensaje> getEnviados(Usuario u) {
        return em.createQuery("SELECT m FROM Mensaje m WHERE m.emisor = :usr AND m.deletedByEmisor = FALSE").
                setParameter("usr", u).getResultList();
    }

    @Override
    public List<Usuario> getPossibleRecipients(Class cl) {
        List<Usuario> ret = new LinkedList<>();

        if (cl.equals(Paciente.class
        )) {
            ret.addAll(getMedicos());
            ret.addAll(getAdministrativos());
        }

        if (cl.equals(Medico.class
        )) {
            ret.addAll(getPacientes());
            ret.addAll(getAdministrativos());
        }

        if (cl.equals(Administrador.class
        )) {
            ret.addAll(getPacientes());
            ret.addAll(getMedicos());
            ret.addAll(getAdministrativos());
            ret.addAll(getAdministradores());
        }

        if (cl.equals(Administrativo.class
        )) {
            ret.addAll(getPacientes());
            ret.addAll(getMedicos());
        }
        return ret;
    }

    @Override
    public Error añadir_Enfermedad(Enfermedad e) {
        Enfermedad enf = em.find(Enfermedad.class, e.getNombre());
        if (enf != null) {
            return Error.ERROR_ENFERMEDAD_REPETIDA;
        } else {
            em.persist(e);
        }
        return Error.NO_ERROR;
    }

    @Override
    public Error eliminar_Enfermedad(Enfermedad e) {
        Enfermedad enf = em.find(Enfermedad.class, e.getNombre());
        if (enf == null) {
            return Error.ERROR_USUARIO_INEXISTENTE;
        } else {
            em.remove(enf);
        }
        return Error.NO_ERROR;
    }

    @Override
    public Error eliminar_Cita(Cita c) {
        Cita cita = em.find(Cita.class, c.getId_cita());
        em.remove(cita);
        return Error.NO_ERROR;
    }

    @Override
    public Error Añadir_Cita(Cita c, Medico m, Paciente p) {
        em.persist(c);
        Error e = compruebaLogin(m);
        if (e != Error.NO_ERROR) {
            return e;

        } else {
            Medico medico = em.find(Medico.class, m.getDni());
            List<Cita> citas = medico.getCita();
            Paciente paciente = em.find(Paciente.class, p.getDni());

            if (citas.contains(c)) {
                return Error.ERROR_CITA_REPETIDA;
            } else {
                Cita cita = em.find(Cita.class, c.getId_cita());
                cita.setMedico(medico);
                cita.setPaciente(paciente);
                em.merge(cita);

                citas.add(c);
                medico.setCita(citas);
                em.merge(medico);
                return Error.NO_ERROR;

            }
        }
    }

    @Override
    public List<Cita> getCitasM(Medico m) {
        return em.createQuery("SELECT u FROM Cita u WHERE u.medico = :dni", Cita.class
        ).
                setParameter("dni", m).getResultList();
    }

    @Override
    public List<Cita> getCitasP(Paciente p) {
        return em.createQuery("SELECT u FROM Cita u WHERE u.paciente = :dni", Cita.class
        ).
                setParameter("dni", p).getResultList();
    }

    @Override
    public List<Cita> getCitas() {
        return em.createQuery("SELECT u FROM Cita u", Cita.class
        ).getResultList();
    }

    @Override
    public List<Enfermedad> getEnfermedad() {
        return em.createQuery("SELECT u FROM Enfermedad u", Enfermedad.class
        ).getResultList();
    }

    @Override
    public List<ListaDeEspera> getListaEspera() {
        return em.createQuery("SELECT u FROM ListaDeEspera u", ListaDeEspera.class
        ).getResultList();
    }

    @Override
    public List<Medico> getMedicos() {
        return em.createQuery("SELECT u FROM Medico u", Medico.class
        ).getResultList();
    }

    @Override
    public List<Paciente> getPacientes() {
        return em.createQuery("SELECT u FROM Paciente u", Paciente.class
        ).getResultList();
    }

    @Override
    public List<Administrativo> getAdministrativos() {
        return em.createQuery("SELECT u FROM Administrativo u", Administrativo.class
        ).getResultList();
    }

    @Override
    public List<Administrador> getAdministradores() {
        return em.createQuery("SELECT u FROM Administrador u", Administrador.class
        ).getResultList();
    }

    @Override
    public List<Administrador> getAdministrador() {
        return em.createQuery("SELECT u FROM Administrador u", Administrador.class
        ).getResultList();
    }

    @Override
    public List<HistoriaClinica> getHistoriaClinica() {
        return em.createQuery("SELECT u FROM HistoriaClinica u", HistoriaClinica.class
        ).getResultList();
    }

    @Override
    public List<Usuario> getTodosUsuariosDeTipo(Class type) {
        return em.createQuery(
                "SELECT u FROM Usuario u", type).getResultList();
    }
}
